import random

from strange_io import SIGNATURE, get_format, _load_bytearray

class StrangeText:
    def __init__(self):
        self.encoding = [' ','q','w','e','r','t','y','u','i','o','p','a','s','d','f','g','h','j','k','l','z','x','c',
                         'v','b','n','m','Q','W','E','R','T','Y','U','I','O','P','A','S','D','F','G','H','J','K','L',
                         'Z','X','C','V','B','N','M','1','2','3','4','5','6','7','8','9','0','!','@','#','$','%','^',
                         '&','*','=','+','-','_','?','/','\\','|',';',':','\'','"','`','~',',','.','(',')','[',']','{',
                         '}','\n','<','>','\t']
        self.text: bytes = b''

    def dump(self) -> str:
        """ Get decoded text """
        return self.decode(self.text)

    def load(self, text: str):
        """ Encode and store text """
        self.text = self.encode(text)

    def dump_bytes(self) -> bytes:
        """ Get encoded text """
        return self.text

    def load_bytes(self, text: bytes):
        """ Store encoded text """
        self.text = text

    def dump_file(self, path):
        """ Dump text to file """
        out_file = bytearray()
        header = bytearray(SIGNATURE + b'text\n')
        out_file += header
        out_file += self.text
        with open(path, 'wb') as f:
            f.write(out_file)
            f.close()

    def load_file(self, path, ignore_type=False):
        """ Load text from file """
        if get_format(path) == b'text' or ignore_type:
            file = _load_bytearray(path, remove_type=True)
            out = file
            self.text = out
        else:
            raise TypeError('Attempted to load non-text document as text document')

    def encode(self, text: str) -> bytes:
        out = bytearray()
        for i in text:
            if i not in self.encoding:
                raise ValueError(f'Invalid character: {i}')
            for j1, j2 in enumerate(self.encoding):
                if i == j2:
                    out += bytearray([j1])
        return out

    def decode(self, text: bytes) -> str:
        out = ''
        for i in text:
            if i >= len(self.encoding):
                raise ValueError(f'Unknown character {i}')
            out += self.encoding[i]
        return out

    def clear(self):
        self.text = b''

#region str-like
    def append(self, text:str|bytes):
        if type(text) == str:
            self.text += self.encode(text)
        elif type(text) == bytes:
            self.text += text
        else:
            raise TypeError

    def __len__(self):
        return len(self.text)

    def __str__(self):
        return self.decode(self.text)
#endregion

class UltraStrangeText(StrangeText):
    """ Ultra = Extension """

    def __init__(self):
        super().__init__()
        random.shuffle(self.encoding)

    def encode(self, text: str) -> bytes:
        out = bytearray()
        encoded_text = super().encode(text)
        temp = bytearray()
        for _, character in enumerate(self.encoding):
            temp += character.encode()
        out += len(temp).to_bytes(1, 'big')
        out += temp
        del temp
        out += encoded_text
        del encoded_text
        return bytes(out)

    def decode(self, text: bytes, overide_encoding:bool=True) -> str:
        text = bytearray(text)
        encoding_len = text[0]
        del text[0]
        if overide_encoding:
            for i in range(encoding_len):
                self.encoding[i] = bytes([text[0]]).decode()
                del text[0]
        else:
            for _ in range(encoding_len):
                del text[0]
        out = super().decode(bytes(text))
        return out

    def dump_file(self, path):
        """ Dump text to file """
        out_file = bytearray()
        header = bytearray(SIGNATURE + b'uText\n')
        out_file += header
        out_file += self.text
        with open(path, 'wb') as f:
            f.write(out_file)
            f.close()

    def load_file(self, path, ignore_type=False):
        """ Load text from file """
        if get_format(path) == b'uText' or ignore_type:
            file = _load_bytearray(path, remove_type=True)
            out = file
            self.text = out
        else:
            raise TypeError('Attempted to load non-uText document as uText document')

#region str-like
    def append(self, text:str|bytes):
        if type(text) == str:
            self.text += self.encode(text)
        elif type(text) == bytes:
            self.text += text
        else:
            raise TypeError

    def __len__(self):
        return len(self.decode(self.text))

    def __str__(self):
        return self.decode(self.text)
#endregion
